/* Copyright 2014 Jan Wolter */

#include "stack.h"
#include "game.h"

char *gamename= "Fifteens";
char *gameopt= "";
Stack *gathered= NULL;

void printBlockTen(FILE *f);

int nplayed= 0;
int nwon= 0;
int cardsleft= 0;

#define MAXTAB 52
Card *tableau[MAXTAB];
#define NTAB  16
int ntab;
extern int n_opt;

int left;

Stack *dck;

int play(int);

void setopt(char c) {}

int domove(int n, int *move, int depth)
{
    Card *old[10];
    if (verbose > 2) printf("%d: Doing move ", depth);
    for (int i= 0; i < n; i++)
    {
	if (verbose > 2) putchar(rankletter[tableau[move[i]]->n]);
	old[i]= tableau[move[i]];
	tableau[move[i]]= popStack(dck);
    }
    if (verbose > 2) putchar('\n');

    int v= visitedState(currstate());
    if (v < 0)
    {
	if (verbose > 1) printf("Abandoned\n");
	return 1;
    }

    if (v == 0 && play(depth+1)) return 1;

    if (verbose > 2) printf("%d: Undoing move ",depth);
    for (int i= 0; i < n; i++)
    {
	pushStack(dck, tableau[move[i]]);
	tableau[move[i]]= old[i];
	if (verbose > 2) putchar(rankletter[tableau[move[i]]->n]);
    }
    if (verbose > 2) putchar('\n');

    return 0;
}

int play(int depth)
{
    int move[10];

    /* Can we remove four of T,J,Q,K?
     * These are always safe moves, so if removing them doesn't
     * lead to a win, then nothing else will, so we give up.
     */

    for (int j= 10; j <= 13; j++)
    {
	int n= 0;
	for (int i= 0; i < ntab; i++)
	    if (tableau[i] != NULL && tableau[i]->n == j)
	    {
		move[n++]= i;
		if (n == 4) 
		    return domove(n, move, depth);
	    }
    }

    /* Fifteens.  Not safe moves. */
    int n= 0, sum= 0, i= 0;
    for (;;)
    {
	while (i >= ntab)
	{
	    if (n == 0)
		return 0;
	    i= move[--n]+1;
	    sum-= tableau[move[n]]->n;
	}

	if (tableau[i] == NULL || tableau[i]->n >= 10 ||
		sum + tableau[i]->n > 15)
	{
	    i++;
	    continue;
	}
	move[n++]= i;
	sum+= tableau[i]->n;
	if (sum < 15)
	{
	    i++;
	    continue;
	}
	/* Found a 15 */
	if (domove(n, move, depth))
	    return 1;

	/* It failed - pull out the last card added */
	sum-= tableau[i]->n;
	i= move[--n]+1;
    }
}

/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    int i;
    ntab= (n_opt > 0) ? n_opt : NTAB;

    /* Could set things like ndeck, nsuit here */

    /* Build and shuffle the deck */
    dck= mixcards(arif, over, cut);

    /* Create the tableau */
    for (i= 0; i < ntab; i++)
	tableau[i]= popStack(dck);

    if (verbose > 1) printBlockTen(stdout);

    play(0);

    int left= dck->n;
    for (int i= 0; i < ntab; i++)
	if (tableau[i]) left++;

    int win= (left == 0);

    nplayed++;
    cardsleft+= left;
    if (win) nwon++;

    if (verbose > 0) printf("Left=%d\n", left);

    freeStack(dck);
}

/* Not implemented (yet?) */
void gatherAll()
{
}

void printBlockTen(FILE *f)
{
    printf("Tableau:");
    for (int i= 0; i < ntab; i++)
    {
	fputc(' ',f);
	if (tableau[i] == NULL)
	    fputc('_',f);
	else
	    fputc(rankletter[tableau[i]->n],f);
    }
    fputc('\n',f);
    printStack(f, "Deck", dck);
}

void summary()
{
    printf("Played:     %d games\n",nplayed);
    printf("Won:        %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Cards Left: %.2f\n",(float)cardsleft/nplayed);
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* count for number of cards of each rank on tableau, count of cards in
     * stock.
     */
    char buf[20];

    /* Numbers of cards stock (plus one) */
    buf[0]= dck->n + 1;

    /* Numbers of cards of each rank plus one */
    for (int i= 1; i <= 13; i++)
	buf[i]= 1;
    for (int i= 0; i < ntab; i++)
	if (tableau[i])
	    buf[tableau[i]->n]++;

    buf[14]= '\0';

    return strdup(buf);
}
